package top.aprillie.mamabike.bike.service;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.aprillie.mamabike.bike.dao.BikeMapper;
import top.aprillie.mamabike.bike.entity.Bike;
import top.aprillie.mamabike.bike.entity.BikeLocation;
import top.aprillie.mamabike.bike.entity.BikeNoGen;
import top.aprillie.mamabike.common.exception.MaMaBikeException;
import top.aprillie.mamabike.common.utils.DateUtil;
import top.aprillie.mamabike.common.utils.RandomNumberCode;
import top.aprillie.mamabike.fee.dao.RideFeeMapper;
import top.aprillie.mamabike.fee.entity.RideFee;
import top.aprillie.mamabike.record.dao.RideRecordMapper;
import top.aprillie.mamabike.record.entity.RideRecord;
import top.aprillie.mamabike.user.dao.UserMapper;
import top.aprillie.mamabike.user.entity.User;
import top.aprillie.mamabike.user.entity.UserElement;
import top.aprillie.mamabike.wallet.dao.WalletMapper;
import top.aprillie.mamabike.wallet.entity.Wallet;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Author: illure
 * @Description:
 * @Date: Created in 23:02 2018/2/16
 * @Modified By:
 */
@Service("bikeServiceImpl")
@Slf4j
public class BikeServiceImpl implements BikeService {

    private static final Byte NOT_VERYFY = 1;//未认证
    private static final Object BIKE_UNLOCK = 2; //单车解锁
    private static final Object BIKE_LOCK = 1;//单车锁定

    private static final Byte RIDE_END = 2;//骑行结束

    @Autowired
    private BikeMapper bikeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RideRecordMapper rideRecordMapper;
    @Autowired
    private WalletMapper walletMapper;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RideFeeMapper rideFeeMapper;

    /**
     * 生成单车
     *
     * @throws MaMaBikeException
     */
    @Transactional
    @Override
    public void generateBike() throws MaMaBikeException {
        //单车编号生成sql
        BikeNoGen bikeNoGen = new BikeNoGen();
        bikeMapper.generateBikeNo(bikeNoGen);
        //生成单车
        Bike bike = new Bike();
        bike.setType((byte) 1);
        bike.setNumber(bikeNoGen.getAutoIncNo());
        bikeMapper.insertSelective(bike);
    }

    /**
     * 解锁单车
     *
     * @param currentUser
     * @param bikeNo
     * @throws MaMaBikeException
     */
    @Override
    @Transactional
    public void unLockBike(UserElement currentUser, Long bikeNo) throws MaMaBikeException {
        try {
            // 校验用户是否已经（实名认证 押金）
            User user = userMapper.selectByPrimaryKey(currentUser.getUserId());
            if (user.getVerifyFlag().equals(NOT_VERYFY)) {
                throw new MaMaBikeException("用户尚未认证");
            }
            // 检查用户是否有正在进行的骑行记录
            RideRecord record = rideRecordMapper.selectRecordNotClosed(currentUser.getUserId());
            if (record != null) {
                throw new MaMaBikeException("存在未关闭骑行订单");
            }
            // 检查用于钱包余额是否大于一元
            Wallet wallet = walletMapper.selectByUserId(currentUser.getUserId());
            if (wallet.getRemainSum().compareTo(new BigDecimal(1)) < 0) {
                throw new MaMaBikeException("余额不足");
            }
            // 推送单车 进行解锁
//        BaiduPushUtil.pushMsgToSingleDevice(currentUser, "{\"title\":\"TEST\", \"description\":\"Hello Baidu push!\"}");
            // 修改MongoDB中单车状态
            Query query = Query.query(Criteria.where("bike_no").is(bikeNo));
            Update update = Update.update("status", BIKE_UNLOCK);
            mongoTemplate.updateFirst(query, update, "bike-position");
            // 建立骑行订单 记录骑行时间 （记录骑行轨迹 单车上报坐标）
            RideRecord rideRecord = new RideRecord();
            rideRecord.setBikeNo(bikeNo);
            String recordNo = System.currentTimeMillis() + RandomNumberCode.randomNo();
            rideRecord.setRecordNo(recordNo);
            rideRecord.setStartTime(new Date());
            rideRecord.setUserid(currentUser.getUserId());
            rideRecordMapper.insertSelective(rideRecord);
        } catch (Exception e) {
            log.error("fail to unlock bike", e);
            throw new MaMaBikeException("单车解锁失败");
        }
    }

    /**
     * 锁车 骑行结束
     *
     * @param bikeLocation
     * @throws MaMaBikeException
     */
    @Override
    @Transactional
    public void lockBike(BikeLocation bikeLocation) throws MaMaBikeException {
        // 结束订单 计算骑行时间存订单
        try {
            RideRecord record = rideRecordMapper.selectBikeRecordOnGoing(bikeLocation.getBikeNumber());
            if (record == null) {
                throw new MaMaBikeException("骑行记录不存在");
            }
            Long userid = record.getUserid();
            //查询单车类型 查询计价信息
            Bike bike = bikeMapper.selectByBikeNo(bikeLocation.getBikeNumber());
            if (bike == null) {
                throw new MaMaBikeException("单车不存在");
            }
            RideFee fee = rideFeeMapper.selectBikeTypeFee(bike.getType());
            if (fee == null) {
                throw new MaMaBikeException("计费信息异常");
            }
            BigDecimal cost = BigDecimal.ZERO;
            record.setEndTime(new Date());
            record.setStatus(RIDE_END);
            Long min = DateUtil.getBetweenMin(new Date(), record.getStartTime());
            record.setRideTime(min.intValue());
            int minUnit = fee.getMinUnit();
            int intMin = min.intValue();
            if (intMin / minUnit == 0) {
                //不足一个时间单位 按照一个时间单位算
                cost = fee.getFee();
            } else if (intMin % minUnit == 0) {
                //整除 了时间单位 直接计费
                cost = fee.getFee().multiply(new BigDecimal(intMin / minUnit));
            } else if (intMin % minUnit != 0) {
                //不整除 + 1 补足一个时间单位
                cost = fee.getFee().multiply(new BigDecimal((intMin / minUnit) + 1));
            }
            record.setRideCost(cost);
            rideRecordMapper.updateByPrimaryKeySelective(record);
            //钱包扣费
            Wallet wallet = walletMapper.selectByUserId(userid);
            wallet.setRemainSum(wallet.getRemainSum().subtract(cost));
            walletMapper.updateByPrimaryKeySelective(wallet);
            //修改MongoDB中单车状态为锁定
            Query query = Query.query(Criteria.where("bike_no").is(bikeLocation.getBikeNumber()));
            Update update = Update.update("status", BIKE_LOCK).set("location.coordinates", bikeLocation.getCoordinates());
            mongoTemplate.updateFirst(query, update, "bike-position");
        } catch (MaMaBikeException e) {
            log.error("fail to unlock bike", e);
            throw new MaMaBikeException("单车锁定失败");
        }
    }

    /**
     * 上传单车坐标
     *
     * @param bikeLocation
     * @throws MaMaBikeException
     */
    @Override
    public void reportLocation(BikeLocation bikeLocation) throws MaMaBikeException {
        try {
            //数据库中查询该单车尚未完结的订单
            RideRecord record = rideRecordMapper.selectBikeRecordOnGoing(bikeLocation.getBikeNumber());
            if (record == null) {
                throw new MaMaBikeException("骑行记录不存在");
            }
            //查询MongoDB中是否已经有骑行的坐标记录数据
            DBObject obj = mongoTemplate.getCollection("ride_contrail")
                    .findOne(new BasicDBObject("record_no", record.getRecordNo()));
            //没有 插入       已经存在 添加坐标
            if (obj == null) {
                List<BasicDBObject> list = new ArrayList<>();
                BasicDBObject temp = new BasicDBObject("loc", bikeLocation.getCoordinates());
                list.add(temp);
                BasicDBObject insertObj = new BasicDBObject("record_no", record.getRecordNo())
                        .append("bike_no", record.getBikeNo())
                        .append("contrail", list);
                mongoTemplate.insert(insertObj, "ride_contrail");
            } else {
                Query query = new Query(Criteria.where("record_no").is(record.getRecordNo()));
                Update update = new Update().push("contrail", new BasicDBObject("loc", bikeLocation.getCoordinates()));
                mongoTemplate.updateFirst(query, update, "ride_contrail");
            }
        } catch (MaMaBikeException e) {
            log.error("fail to report location", e);
            throw new MaMaBikeException("上报坐标失败");
        }
    }
}
